module.exports = {
  /**
   * 运行Python或Js文件
   *
   * @param {object}param0 选项
   * @param {string}param0.filePath 主Python文件的所在路径
   * @param {object}param0.togetherWith 选项
   * @param {string[]}param0.togetherWith.files 主Python文件依赖的其他文件
   * @param {boolean}[param0.togetherWith.relative=true] files是否相对于filePath所在目录，默认为true
   * @param {boolean}param0.isPython 是否为Python，默认根据后缀自动判断
   * @param {*}[param0.msgTo=[]] Python中 "json.loads(sys.stdin.read())"接收; NodeJs中 process.on('message',(msgTo)=>{ ...to do}) 接收msgTo
   * @param {string}[param0.atDir=filePath所在目录] 要在哪个目录下运行Python文件，默认为原目录
   * @param {string[]}[param0.copyFiles=[]] 需要拷贝到atDir运行的文件(夹)，运行后会删除临时拷过来的文件
   * @param {Function}[param0.onMsg=(result)=>0] 对进程发送的信息进行处理的函数, 参数为发回来的信息, (先callBack后resolve)
   * @param {boolean}[param0.killOnMsg=false] 是否在收到1条信息后就kill这个process
   * @param {boolean}[param0.returnRaw=false] Python返回原始数据，不将字符串转为Json, 不对js生效
   * @returns {Promise}返回Pyhton中 print(json.dumps(result))中的result，或者如果没有结果的话，在python文件exit时返回Undefined
   * @description 在Python中使用"json.loads(sys.stdin.read())"接收msgTo (需要import sys;import json; )
   * @description 在NodeJs中 process.on('message',(msgTo)=>{ ...to do}) 接收msgTo
   * @description 在Python中使用"print(json.dumps(result))" 返回结果 (需要import json; )
   * @description 在NodeJs中 process.send(result) 来返回结果
   */
  runScript: function ({
    filePath: originPath,
    togetherWith: { files: env = [], relative = true } = {},
    atDir = require('path').dirname(originPath),
    msgTo = [],
    isPython = /py$/i.test(require('path').extname(originPath)),
    copyFiles = [],
    onMsg = () => 0,
    killOnMsg = false,
    returnRaw = false
  }) {
    return new Promise((resolve, reject) => {
      const [Path, Fs, { PythonShell }, childProcess] = [
        require('path'),
        require('fs-extra'),
        require('python-shell'),
        require('child_process')
      ]
      const [runOrigin, newPyPath, newEnvPath, newCopyFilesPath] = [
        atDir === Path.dirname(originPath), // 是否在Python文件的原位置运行
        Path.join(atDir, Path.basename(originPath)), // 新的Python文件位置
        [...env].map((origin) => {
          // 新的Env文件位置
          if (relative) {
            return Path.join(atDir, origin)
          } else {
            return Path.join(
              atDir,
              Path.relative(Path.dirname(originPath), origin)
            )
          }
        }),
        [...copyFiles].map((e) => Path.join(atDir, Path.basename(e)))
      ]
      if (!runOrigin) {
        // 如果不是在原位置运行
        Fs.copySync(originPath, newPyPath, { overwrite: true }) // 把Python原文件拷贝过去
        newEnvPath.forEach((dest, i) => {
          // 把环境文件拷贝过去
          if (relative) {
            Fs.copySync(Path.join(Path.dirname(originPath), env[i]), dest, {
              overwrite: true
            })
          } else {
            Fs.copySync(env[i], dest, { overwrite: true })
          }
        })
      }
      copyFiles.forEach((e, i) =>
        Fs.copySync(e, newCopyFilesPath[i], { overwrite: true })
      )
      const removeFiles = () => {
        // 最后删除文件
        if (!runOrigin) {
          Fs.removeSync(newPyPath)
          newEnvPath.forEach((e) => Fs.removeSync(e))
        }
        newCopyFilesPath.forEach((e) => Fs.removeSync(e))
      }
      if (isPython) {
        const pyshell = new PythonShell(newPyPath, { mode: 'text' })
        pyshell.send(JSON.stringify(msgTo)) // 在Python文件中sys.stdin.read()
        pyshell.on('message', (msg) => {
          // 在Python文件中 print(json.dumps(result))
          try {
            const result = returnRaw ? msg : JSON.parse(msg)
            onMsg(result)
            resolve(result) // 有两个resolve
            killOnMsg && pyshell.kill()
          } catch (error) {
            // 如果不能parse，那么可能不是主动print
          }
        })
        pyshell.on('error', (msg) => reject(msg))
        pyshell.end((err, exitCode) => {
          console.log('Python run ended:', { err, exitCode })
          resolve()
          removeFiles()
        })
      } else {
        const jsShell = childProcess.fork(newPyPath)
        jsShell.on('message', (msg) => {
          onMsg(msg)
          resolve(msg)
          if (killOnMsg) {
            jsShell.kill()
            console.log('Javascript run ended', { by: 'kill' })
            removeFiles()
          }
        })
        jsShell.send(msgTo)
        jsShell.on('error', (err) => reject(err))
        jsShell.on('exit', (code, signal) => {
          console.log('Javascript run ended:', { code, signal })
          resolve()
          removeFiles()
        })
      }
    })
  },
  /**
   * 运行Docker MongoDb
   *
   * @param {string} [cwd=cwd()/Mongodb] 在哪个目录运行
   * @param {string} [options={'--name': 'my-mongo-db','-v': `${dbPath}:/data/db`,'-p': `${port}:27017`}] 参数
   * @returns {string} 由于执行失败，所以仅返回该指令
   */
  async runDockerMongo(
    cwd = require('path').join(process.cwd(), 'MongoDb'),
    options
  ) {
    const { getUnOccupiedPort } = require('./express-wss')
    const path = require('path')
    const port = await getUnOccupiedPort(27017)
    const dbPath = path.join(process.cwd(), 'MongoDb/database')
    options = options || {
      '--name': 'my-mongo-db',
      '-v': `${dbPath}:/data/db`,
      '-p': `${port}:27017`
    }
    let args = 'docker run'
    for (const key in options) {
      args += ` ${key} ${options[key]}`
    }
    args += ` -d mongo:latest`
    console.log(args)
    return args
    // return await new Promise((resolve, reject) => {
    //   require('child_process').exec(args, { cwd }, (error, stdout, stderr) => {
    //     console.log({ 'mongo-server running at ': { port, dbPath } })
    //     resolve({ error, stdout, stderr, port, dbPath })
    //   })
    // })
  }
}
